noip模拟赛2017.7.28

题目名称 九九归一 LCA 的统计 四驱兄弟
目录 mulone lcastat letsandgo
可执行文件名 mulone lcastat letsandgo
每个测试点时限 1s 1s 1s
内存限制 128M 128M 128M
测试点数目 10 10 10
每个测试点分值 10 10 10
是否有部分分
题目类型 传统 传统 传统

九九归一

【问题描述】

萌蛋在练习模𝑛意义下的乘法时发现,总有一些数,在自乘若干次以后,会 变成 1。例如𝑛 = 7,那么5 × 5 𝑚𝑜𝑑 7 = 4,4 × 5 𝑚𝑜𝑑 7 = 6,6 × 5 𝑚𝑜𝑑 7 = 2,2 × 5 𝑚𝑜𝑑 7 = 3,3 × 5 𝑚𝑜𝑑 7 = 1。如果继续乘下去,就会陷入循环当中。 萌蛋还发现,这个循环的长度经常会是φ(𝑛),即小于𝑛且与𝑛互质的正整数 的个数。例如,φ(7) = 6,而上述循环的长度也是 6,因为 5,4,6,2,3,1 共有 6 个 数。 再如𝑛 = 6,那么5 × 5 𝑚𝑜𝑑 6 = 1。这个循环的长度很短,只有 2,而恰好 φ(6) = 2。 然而,对于某些情况,虽然循环的长度可以是φ(𝑛),但存在比φ(𝑛)更小的长 度:例如𝑛 = 7,而2 × 2 𝑚𝑜𝑑 7 = 4,4 × 2 𝑚𝑜𝑑 7 = 1,循环的长度只有 3。当然, 6 也可以是一个循环的长度。 假设已知了𝑛,我们称数𝑎神奇的,当且仅当关于数𝑎的循环长度可以是φ(𝑛), 而且不存在比φ(𝑛)更小长度的循环。例如对于𝑛 = 7,5 是神奇的,而 2 不是神 奇的。 现在给出𝑛和𝑞次询问,每次询问给出𝑎,问𝑎是否是神奇的。

【输入文件】

第一行两个整数𝑛 𝑞。 第二行有𝑞个整数,每个表示一个𝑎。
【输出文件】
输出𝑞个字符,1 表示这个数是神奇的,0 表示这个数不是神奇的。

【输入样例】

7 3 5 2 0

【输出样例】

100

【数据规模和约定】

对于 30%的数据,𝑛 ≤ 1,000。
对于 60%的数据,𝑛 ≤ 100,000。
对于 80%的数据,𝑛 ≤ 1,000,000。
对于 100%的数据,𝑛 ≤ 10,000,000,𝑞 ≤ 100,000,0 ≤ 𝑎 < 𝑛。

Solution

和昨天的哪一题有点像,还是比较裸的,题目的要求已经说的很清楚了,要求

且满足任意一个φ(𝑛)的因子不满足上述式子即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
int n,q,phin;
bool flag[10000010];
int phi[10000010],prime[10000010],Div[1000010];
void Pre_Phi()
{
for(int i=2;i<=10000010;i++)
{
if(!flag[i])prime[++prime[0]]=i,phi[i]=i-1;
for(int j=1;j<=prime[0]&&i*prime[j]<=10000000;j++)
{
flag[i*prime[j]]=true;
phi[i*prime[j]]=phi[i]*(prime[j]-1);
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
}
void Pre_Check()
{
for(int i=2;i<=sqrt(phin);i++){
if(phin%i==0)
Div[++Div[0]]=i,Div[++Div[0]]=phin/i;
}
}
int quickpow(int x,int y)
{
int rtn=1;
while(y)
{
if(y&1)rtn=((LL)rtn*x)%n;
x=((LL)x*x)%n;
y>>=1;
}
return rtn%n;
}
int main()
{
freopen("mulone.in","r",stdin);
freopen("mulone.out","w",stdout);
Pre_Phi();
scanf("%d%d",&n,&q);
phin=phi[n];
Pre_Check();
while(q--)
{
int a,f=0;
scanf("%d",&a);
for(int i=1;i<=Div[0];i++)
{
int d=Div[i];
int c=quickpow(a,d);
if(c==1){
printf("0");
f=1;
break;
}
}
if(f==0&&quickpow(a,phin)==1)printf("1"),f=1;
if(f==0)printf("0");
}
return 0;
}
/*
7 3
5 2 0
*/

LCA 的统计

【问题描述】

萌蛋有一棵𝑛个节点的有根树,其根节点为 1。除此之外,节点𝑖的父节点 为𝑝𝑖。每个点上都有一个权值,节点𝑖的权值是𝑤𝑖。 萌蛋知道你一定知道什么叫做祖先(从根到某个点的路径上的每个点都是 这个点的祖先,包括它本身),也一定知道什么叫做最近公共祖先(两个点的最 近公共祖先是某个点,这个点同时是两个点的祖先,且离根最远)。 现在给出这棵树,你需要求出:
image
其中𝐿𝐶𝐴(𝑖,𝑗)表示点𝑖与点𝑗的最近公共祖先。 由于答案可能很大,你只需要输出它对 1,000,000,007 取模的结果。

【输入文件】

第一行为两个整数𝑛 𝑤1。 第二行到第𝑛行,第𝑖行有两个整数𝑝𝑖 𝑤𝑖。
【输出文件】
输出只有一行,为一个整数,表示所求答案对 1,000,000,007 取模的结果。

【输入样例】

2 2
1 1

【输出样例】

17

【样例解释】

1 × 1 × 1 + 1 × 2 × 2 + 2 × 1 × 2 + 2 × 2 × 2 = 17。

【数据规模和约定】

对于30%的数据,𝑛 ≤ 100,𝑤𝑖 ≤ 10。
对于 60%的数据,𝑛 ≤ 1,000,𝑤𝑖 ≤ 1,000。
对于 100%的数据,1 ≤ 𝑛 ≤ 100,000,0 ≤ 𝑤𝑖 ≤ 1,000,000,000,1 ≤ 𝑝𝑖 < 𝑖。

Solution

树上dp,因为这道题今天ak失败,是菜啊,考虑一个点作为LCA的情况,根据题目给出的式子有三种情况:
1.i,j,k均为一个点,那么直接^3;
2.i,j重合,他的所有儿子的都对答案有贡献,别忘记乘二;
3.最麻烦的是i,j分别在两个不同的子树中,考虑一个二叉树,编号从1~7,当1是LCA时,你可以手动枚举一下,然后合并什么的,就可得到一个式子:ans+=w1(w2+w4+w5)(w3+w6+w7)。那么就应该做出一个子树和,便于统计。对于不是二叉树的,依旧可以通过他的子树统计,统计方法枚举一个儿子,他具有和其他儿子配对的可能性,然后用是s[k]-s[kk]就得到了所有其他儿子的配对数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define mod 1000000007
#define LL long long
using namespace std;
int cnt,n,w1,p[100010];
LL w[100010],s[100010],ans=0;
struct node{
int a,b,nt;
}e[1000010];
void add(int x,int y){
cnt++;
e[cnt].a=x,e[cnt].b=y,e[cnt].nt=p[x],p[x]=cnt;
}
void dfs(int k)
{
ans=(ans+((w[k]*w[k])%mod)*w[k]%mod)%mod;
s[k]=w[k];
for(int i=p[k];i;i=e[i].nt)
{
int kk=e[i].b;
dfs(kk);
ans=(ans+((w[k]*w[k])%mod*s[kk])%mod*2%mod)%mod;
s[k]=(s[k]+s[kk])%mod;
}
for(int i=p[k];i;i=e[i].nt)
{
int kk=e[i].b;
ans=(ans+((w[k]*s[kk])%mod*(s[k]+mod-w[k]-s[kk])%mod)%mod)%mod;
}
return;
}
int main()
{
freopen("lcastat.in","r",stdin);
freopen("lcastat.out","w",stdout);
scanf("%d%d",&n,&w[1]);
for(int i=2;i<=n;i++)
{
int fa;
scanf("%d%d",&fa,&w[i]);
add(fa,i);
}
dfs(1);
printf("%lld\n",ans%mod);
return 0;
}

四驱兄弟

【问题描述】

如果你和萌蛋一样,也看过《四驱兄弟》,你或许会记得,有一局比赛十分 特别,只按照 5 个人中的第 4 名计算成绩。 现在我们将问题扩展一下:一共有𝑛个队员,只按照其中的第𝑘名计算成 绩。而赛车的规则也有所不同:一共有𝑚个赛车,每个赛车装配着 2 个 GP 晶 片的终端,且第𝑖个赛车预期到达终点的时间为𝑎𝑖。(注:不同赛车上的终端可 以对应着相同的 GP 晶片,但不会 2 个都相同;任何赛车上的 2 个终端对应的 GP 晶片都是不同的) 比赛开始时,𝑛个队员依次选择自己的赛车。对于每个队员,他可以选择开 启 GP 晶片功能或不开启。如果开启,那么 2 个终端对应的 GP 晶片就会建立连 接,且这个赛车的比赛用时就是预期时间𝑎𝑖;如果不开启,那么 GP 晶片不会 建立连接,但是这个赛车的比赛用时将会非常长(可以认为是无穷)。甚至,他 可以放弃比赛,这样他不会占用任何赛车,但是当然比赛用时也会被认为是无 穷。 任何时候,一旦存在若干个(至少 3 个)晶片 A,B,C,…,X 满足:A 与 B 建 立了连接,B 与 C 建立了连接,……,X 与 A 建立了连接(即形成了循环连接 的情况) ,那么处理系统就会崩溃。这是非常可怕的,我们宁可让比赛用时变为 无穷也不能让系统崩溃。 现在给出队员和赛车的信息,请输出最优情况下的成绩(即第𝑘小的比赛时 间的最小值)。 为了增大难度,𝑘并不是给出的,而是你需要对于1 ≤ 𝑘 ≤ 𝑛的所 有的𝑘输出答案。

【输入文件】

第一行为两个整数𝑛 𝑚。 接下来𝑚行,每行描述了一个赛车,格式为空格隔开的一个整数和两个字符 串,分别是𝑎𝑖和它的两个终端对应的 GP 晶片名。

【输出文件】

𝑛行,每行一个整数,第𝑖行表示𝑖 = 𝑘时的答案。特别地,如果答案是无穷, 输出 INF。

【输入样例】

3 3
95 GP_1 GP_2
100 GP_1 gp@3
100 gp@3 GP_2

Solution

打的舒舒服服,是开始看这道题的时候,就感觉到了这个系列赛的典型特点,题目背景是什么鬼??强行四驱车一波….吐槽完毕,还是比较裸的一道题,都已经说了无环,以我现在这个知识水平也就最小生成树了.还有什么hash 离散化的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#define ULL unsigned long long
#define LL long long
using namespace std;
struct node{
ULL a,b;
LL w;
int gp1,gp2;
friend bool operator < (node a,node b){
return a.w<b.w;
}
}car[100010];
char gp[2][100];
int fa[100010];
ULL c[200010];
int findf(int x){
if(fa[x]==x)return fa[x];
else return fa[x]=findf(fa[x]);
}
void mergef(int x,int y)
{
int tx=findf(x),ty=findf(y);
fa[tx]=ty;
}
int main()
{
freopen("letsandgo.in","r",stdin);
freopen("letsandgo.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld",&car[i].w);
scanf("%s%s",gp[0],gp[1]);
ULL gp1=0,gp2=0;
int len=strlen(gp[0]);
for(int j=0;j<len;j++)gp1=gp1*255+gp[0][j];
len=strlen(gp[1]);
for(int j=0;j<len;j++)gp2=gp2*255+gp[1][j];
c[++c[0]]=gp1;
c[++c[0]]=gp2;
car[i].a=gp1,car[i].b=gp2;
}
sort(car+1,car+1+m);
sort(c+1,c+c[0]+1);
int tot=0;
for(int i=1;i<=c[0];i++)if(c[i]!=c[i-1])c[++tot]=c[i];
for(int i=1;i<=m;i++)
{
ULL x=car[i].a,y=car[i].b;
int g1=lower_bound(c+1,c+1+tot,x)-c;
int g2=lower_bound(c+1,c+1+tot,y)-c;
car[i].gp1=g1,car[i].gp2=g2;
}
for(int i=1;i<=tot;i++)fa[i]=i;
for(int i=1;i<=m&&n>0;i++)
{
if(findf(car[i].gp1)!=findf(car[i].gp2))
{
mergef(car[i].gp1,car[i].gp2);
printf("%lld\n",car[i].w);
n--;
}
}
while(n--)
{
printf("INF\n");
}
//printf("%d",(sizeof(car)+sizeof(fa)+sizeof(c))/1024/1024);
return 0;
}
/*
3 3
95 GP_1 GP_2
100 GP_1 gp@3
100 gp@3 GP_2
10 10
100 a b
165 asd df
98 sdg gsfd
12 fsa er
948 stf th
126 sdf hg
16 srdy gd
546 fgh ty
46 dr jgh
945 ah ty
*/

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 九九归一
    1. 1.1. 【问题描述】
    2. 1.2. 【输入文件】
    3. 1.3. 【输入样例】
    4. 1.4. 【输出样例】
    5. 1.5. 【数据规模和约定】
    6. 1.6. Solution
  2. 2. LCA 的统计
    1. 2.1. 【问题描述】
    2. 2.2. 【输入文件】
    3. 2.3. 【输入样例】
    4. 2.4. 【输出样例】
    5. 2.5. 【样例解释】
    6. 2.6. 【数据规模和约定】
    7. 2.7. Solution
  3. 3. 四驱兄弟
    1. 3.1. 【问题描述】
    2. 3.2. 【输入文件】
    3. 3.3. 【输出文件】
    4. 3.4. 【输入样例】
    5. 3.5. Solution
,